import javax.xml.bind.JAXBException;

public class PlayCounter implements Runnable {
	public int sleepTime = 100; // time in between checks
	public float stdDevWt = (float) 2.; // how much of a standard deviation above average should trigger
	public int minDur = 100; // this is the minimum duration to be considered a non-simultaneous note.
	public int durThresh = 1000; // durational threshold
	public int timeThresh = 6000;
	@Override
	public void run() {
		while (true) {
			// if interaction has started
			if (Main.playing) {
				// if the human is still playing .. see if they have stopped
				if (Main.humanPlaying ) {
//					if last onset is more than X x (rhythmic standard deviation) OR more than some threshold duration
					int lastCol = Main.onsets.length-1; // find last column
					if (Main.onsets.length>1) {
						int[] lastRowDur = toDurations(Main.onsets); // convert to an array of durations (instead of onsets)
						int totalTime = calcTotalTime(lastRowDur);
						if (totalTime>timeThresh) {
							System.out.println("totalTime = "+totalTime);
							response();
						}
						else {
							if (minExists(lastRowDur)) // see if there is are two durations >= minimum duration
							{
								float avg = average(lastRowDur);
								float stdDev = standardDeviation(lastRowDur,avg);					
	//							find last onset
								if ((int)(System.currentTimeMillis())-Main.onsets[lastCol]>(stdDevWt*stdDev+avg)) { // end of human phase ... respond
	//								System.out.println("NEW PHRASE");
	//								System.out.println("System.currentTimeMillis()-Main.rhyIn[lastRow][lastCol] = "+(System.currentTimeMillis()-Main.rhyIn[lastRow][lastCol]));
	//								System.out.println("(stdDevWt*stdDev+avg) = "+(stdDevWt*stdDev+avg));
									response();
								}
							}
							else { // 2 notes .. check minimum threshold
								if ((int)(System.currentTimeMillis()-Main.onsets[lastCol])>durThresh) {
									response();
								}
							}
						}
					}
					else { // 1 note .. check minimum threshold
						if (Main.onsets.length>0&&(int)(System.currentTimeMillis()-Main.onsets[0])>durThresh) {
							response();
						}
					}
				}
			}
			// to minimize the processing, enter a sleep phase in between checks
			try {
				Thread.sleep(sleepTime);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	boolean minExists(int[] array) { // at least two durations of the minimum value
		boolean exists=false;
		boolean exists2=false;
		for (int i=0;i<array.length;i++) {
			if (array[i]>=minDur) {
				if (exists) {
					exists2 = true;
				}
				else {
					exists=true;
				}
			}
		}
		return exists2;
	}
	int calcTotalTime(int[] array) {
		int time = 0;
		
		for (int i=0;i<array.length;i++) {
			time+=array[i];
		}
		return time;
	}
	float average(int[] array) {
		float sum = 0;
		int count = 0;
		float average = 0;
		for (int i=0;i<array.length;i++) {
			if (array[i]>minDur) {
				count++;
				sum += array[i];
			}
		}
		average = sum/count;
		System.out.println("average = "+average);
		return average;
	}
	
	float standardDeviation(int[] array, float avg) {
		float stdDev = 0;
		float DevSqSum = 0;
		int count = 0;
		for (int i=0;i<array.length;i++) {
			if (array[i]>minDur) {
				DevSqSum += (avg-array[i])*(avg-array[i]);
				count++;
			}
		}
		count--;
		stdDev = (float) Math.sqrt(DevSqSum/count);
//		System.out.println("stdDev = "+stdDev);
		return stdDev;
	}
	
	
	int[] toDurations(int[] array) { // change onsets array into durations array (first diff)
		int[] durArray = new int[array.length-1];
		for (int i=0;i<array.length-1;i++) {
			durArray[i]=array[i+1]-array[i];
		}
		return durArray;
	}
	
	void response() {
//		add one last onset
//		TODO:add last input
//		TODO:reinitialize onsets AFTER improv
		System.out.println("END OF PHRASE!!!");
		Main.improvise.stop=false;
//		Main.learning=false;

//		System.out.println("Main.onsets[Main.onsets.length-1] = "+(Main.onsets[Main.onsets.length-1]));
//		System.out.println("Main.onsets[0] = "+Main.onsets[0]);
		Main.improvTime = (int)((Main.onsets[Main.onsets.length-1]-Main.onsets[0]));

		if (Main.improvTime>timeThresh) {
			Main.improvTime=timeThresh;
		}
		
		Main.learnArray = Main.learnArrayTemp;
		Main.learnArrayPointer = Main.learnArrayPointerTemp;
		Main.onsets = new int[0];

		Main.learnArrayPointerTemp=0;
		Main.learnArrayTemp=new int[5000][3];
		
		try {
			Main.improvise.start();
		} catch (JAXBException e) {
			e.printStackTrace();
		}
		try {
			Thread.sleep((int)(1.*Main.improvTime));
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		Main.improvise.stop=true;
		try {
			Thread.sleep((int)(.5*Main.improvTime));
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		System.out.println("DONE IMPROVISING");
		
		Main.improvise.stop=true;
		Main.learning=true;
		

		

		
		
	}

}
